package me.edzh.spring.bootstrap.controller;

import com.yhb.channel.exception.PayChannelException;
import me.edzh.spring.bootstrap.interceptor.TraceIdInterceptor;
import me.edzh.spring.bootstrap.model.Response;
import me.edzh.spring.bootstrap.utils.Constants;
import me.edzh.spring.bootstrap.utils.ResultCode;
import me.edzh.spring.bootstrap.utils.exception.GlobalResponseException;
import me.edzh.spring.bootstrap.utils.exception.ServiceException;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.log4j.MDC;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import static me.edzh.spring.bootstrap.utils.Constants.LOG_TRACE_ID;

/**
 * Created by Edward on 2016/8/10.
 */
@ControllerAdvice
public class GlobalExceptionHandler extends BaseGlobalExceptionHandler {

    @ExceptionHandler(BadSqlGrammarException.class)
    @ResponseStatus(HttpStatus.OK)
    public void handleSqlError(HttpServletRequest req, HttpServletResponse rsp, Exception e) throws Exception {
        logException(e);

        logger.error("err",e);
        Response dto = new Response();
        dto.setCode(ResultCode.ERROR_DB_CODE);
        dto.setMessage("数据库出现错误");
        this.respondData(rsp,dto);
    }

    @ExceptionHandler(GlobalResponseException.class)
    @ResponseStatus(HttpStatus.OK)
    public void handleGlobalResponse(HttpServletRequest req, HttpServletResponse rsp, GlobalResponseException e) throws Exception {
        logException(e);
        this.respondData(rsp, Response.builder()
                .code(e.getCode())
                .message(e.getMessage())
                .data(e.getData())
                .traceId(MDC.get(LOG_TRACE_ID).toString())
                .build());
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public void handleOtherError(HttpServletRequest req, HttpServletResponse rsp, Exception e) throws Exception {
        logger.error("err",e);
        logException(e);

        Response dto = new Response();
        dto.setCode(ResultCode.ERROR_DEFAULT_CODE);
        if (Constants.isDebug()) {
            dto.setMessage("服务器错误" + ExceptionUtils.getStackTrace(e));
        } else {
            dto.setMessage("服务器错误");
        }

        this.respondData(rsp,dto);
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.OK)
    public void handleMethodNotSupportedError(HttpServletRequest req, HttpServletResponse rsp, Exception e) throws Exception {
        logger.error("err",e);

        Response dto = new Response();
        dto.setCode(ResultCode.ERROR_HTTP_METHOD_NOT_SUPPORTED);
        dto.setMessage("当前接口不支持此http method");
        this.respondData(rsp,dto);
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.OK)
    public void handleBodyNotReadableError(HttpServletRequest req, HttpServletResponse rsp, Exception e) throws Exception {
        logException(e);

        logger.error("err",e);

        Response dto = new Response();
        dto.setCode(ResultCode.ERROR_PARAMETER_WRONG);
        dto.setMessage("参数错误");
        this.respondData(rsp,dto);
    }

    @ExceptionHandler(ServiceException.class)
    @ResponseStatus(HttpStatus.OK)
    public void handleServiceException(HttpServletRequest req, HttpServletResponse rsp, ServiceException e) throws Exception {
        logException(e);

        if(e.getNestedException() != null){
            logger.error(e.getMessage());
            e.getNestedException().printStackTrace();
            logger.error("err", e.getNestedException());

        }



        Response dto = new Response();
        dto.setCode(e.getCode());
        dto.setMessage(e.getMessage());
        this.respondData(rsp,dto);
    }

    @ExceptionHandler(PayChannelException.class)
    @ResponseStatus(HttpStatus.OK)
    public void handlePayChannelException(HttpServletRequest req, HttpServletResponse rsp, PayChannelException e) throws Exception {
        logException(e);
        if(e.getNestedException() != null){
            logger.error(e.getMessage());
            e.getNestedException().printStackTrace();
            logger.error("err", e.getNestedException());

        }
        Response dto = new Response();
        dto.setCode(e.getCode() + com.yhb.conf.ResultCode.ERROR_CHANNEL_CODE_UP_THRESHOLD);
        dto.setMessage(e.getMessage());
        this.respondData(rsp,dto);
    }

    private void logException(Exception e) {
        Long code = -1L;
        if (e instanceof ServiceException ) {
            code = ((ServiceException) e).getCode();
        } else if (e instanceof PayChannelException) {
            code = ((PayChannelException) e).getCode();
        } else if (e instanceof GlobalResponseException) {
            code = ((GlobalResponseException) e).getCode();
        }
        logger.error(Constants.getCurrentHandlerName() + (code < 0 ?  " failed. " : " returned. ") + code + ":" + e.getMessage());
        TraceIdInterceptor.logRequestTimeCost(code >= 0);
    }

}
